/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.tabbedproperties.composites;

import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.Namespace;
import org.eclipse.uml2.uml.OpaqueExpression;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.UMLFactory;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.commands.MoveConstraintInListCommand;
import com.cea.papyrus.core.commands.factories.UMLDi2ElementDeleteFactory;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.tabbedproperties.PropertyViewSection;
import com.cea.papyrus.umlutils.Element;

/**
 * Composite that displays the constraints of an element.
 */
public class ConstraintsTableComposite extends DecoratedTableComposite implements ISectionComposite {

	/** key to access UML Data from table items */ 
	public final static String KEY = "constraint_data";
	
	/** name space that owns the constraints */
	private org.eclipse.uml2.uml.Namespace umlNamespace;
	private org.eclipse.uml2.uml.Element umlElement;

	/** parent section for this composite */
	private PropertyViewSection parentSection;

	/**
	 * key to access to Activity image.
	 */
	private final static String CONSTRAINT_IMAGE = PapyrusPlugin.UML_ICONS_16x16+"Constraint.gif";

	private final static String TYPE_PRE  = "PRE";
	private final static String TYPE_BODY = "BODY";
	private final static String TYPE_POST = "POST";
	private final static String TYPE_DEFAULT = "DEFAULT";
	
	/**
	 * The Menu to show all kinds of possible Behavior.
	 */
	private Menu menu;

	/**
	 * Various menu items.
	 */
	private MenuItem preItem, bodyItem, postItem, defaultItem;

	/**
	 * Associated selection listeners.
	 */
	protected SelectionListener preItemListener, bodyItemListener, postItemListener, defaultItemListener;

	
	/**
	 * Default Constructor.
	 */
	public ConstraintsTableComposite(PropertyViewSection parentSection) {
		super("Constraints");
		this.parentSection = parentSection;
	}
	
	/**
	 * Returns the parent section for this ISectionComposite
	 * @return the parent section for this ISectionComposite
	 */
	public PropertyViewSection getParentSection() {
		return parentSection;
	}
	
	/** @{inheritDoc} */
	@Override
	public void setElement(Element utilsElement) {
		super.setElement(utilsElement);
		this.umlNamespace = (Namespace)utilsElement.getUml2Element();
		this.umlElement = utilsElement.getUml2Element();
	}
	
	/**
	 * returns the set of elements selected in the table
	 * @return the set of element selected in the table
	 */
	public TableItem[] getSelection() {
		return table.getSelection();
	}
	
	/**
	 * Adds a selection listener to the table
	 * @param listener listener to add to the table
	 */
	public void addTableSelectionListener(SelectionListener listener) {
		table.addSelectionListener(listener);
	}
	
	/**
	 * Removes a selection listener from the table
	 * @param listener listener to remove from the table
	 */
	public void removeTableSelectionListener(SelectionListener listener) {
		table.removeSelectionListener(listener);
	}
	
	/** @{inheritDoc} */
	@Override
	public void refresh() {
		if( !table.isDisposed()&& (umlNamespace!=null)){
			// Remove parameters from table 
			table.removeAll();

			Iterator<Constraint> it = umlNamespace.getOwnedRules().iterator();
			while (it.hasNext()) {
				Constraint constraint = it.next();
				TableItem item = new TableItem(table, SWT.BORDER);
				item.setData(KEY, constraint);
				// constrained element => umlNamespace = context
				String cType = "";
				if (umlElement instanceof Operation) {
					
					Operation oper = (Operation) umlElement;
					if (oper.getPreconditions().contains(constraint)) {
						cType = TYPE_PRE + "  : ";
					} else if (oper.getPostconditions().contains(constraint)) {
						cType = TYPE_POST + " : ";
					} else if (constraint.equals(oper.getBodyCondition())) {
						cType = TYPE_BODY + " : ";
					} else {
						// default
					}
				}
				
				String ce = getLabelProvider().getText(umlNamespace);
				item.setText(cType + constraint.getLabel()+" -> "+ce);
				item.setImage(getLabelProvider().getImage(constraint));
			}
		}
	}
	
	/**
	 * @{inheritDoc}
	 */
	@Override
	public AdapterFactoryLabelProvider getLabelProvider() {
		return getParentSection().getModelManager().getLabelProvider();
	}

	/**
	 * Adds an additional menu to the addButton. So user can choose which type of trigger to create.
	 */
	private void createMenuForAddButton(){
		menu = new Menu (addButton);

		// add activity menu item
		defaultItem = new MenuItem (menu, SWT.PUSH);
		defaultItem.setText("Constraint");
		defaultItem.setImage(PapyrusPlugin.getImage(CONSTRAINT_IMAGE));
		defaultItem.addSelectionListener(
				defaultItemListener =
					new AddElementSelectionListener(TYPE_DEFAULT));
		
		preItem = new MenuItem (menu, SWT.PUSH);
		preItem.setText("Precondition");
		preItem.setImage(PapyrusPlugin.getImage(CONSTRAINT_IMAGE));
		preItem.addSelectionListener(
				preItemListener =
					new AddElementSelectionListener(TYPE_PRE));

		Operation operation = (Operation) umlElement;
		if (operation.getBodyCondition() == null) {
			// add stateMachine menu item
			bodyItem = new MenuItem (menu, SWT.PUSH);
			bodyItem.setText("Bodycondition");
			bodyItem.setImage(PapyrusPlugin.getImage(CONSTRAINT_IMAGE));
			bodyItem.addSelectionListener(
					bodyItemListener =
						new AddElementSelectionListener(TYPE_BODY));
		}
		
		// add opaqueBehavior menu item
		postItem = new MenuItem (menu, SWT.PUSH);
		postItem.setText("Postcondition");
		postItem.setImage(PapyrusPlugin.getImage(CONSTRAINT_IMAGE));
		postItem.addSelectionListener(
				postItemListener = 
					new AddElementSelectionListener(TYPE_POST));

		addButton.setMenu (menu);
	}
	
	/** @{inheritDoc} */
	@Override
	public void addButtonPressed() {
		
		if (umlElement instanceof Operation) {
			createMenuForAddButton();
			if(!menu.isDisposed())
				menu.setVisible(true);
		
		} else {

			Constraint newConstraint = UMLFactory.eINSTANCE.createConstraint();
			newConstraint.setName("Constraint");
			umlNamespace.getOwnedRules().add(newConstraint);
			newConstraint.getConstrainedElements().add(umlNamespace);
			OpaqueExpression constraintBody = 
				UMLFactory.eINSTANCE.createOpaqueExpression(); 
			constraintBody.getLanguages().clear();
			constraintBody.getBodies().clear();
			constraintBody.getLanguages().add("OCL");
			constraintBody.setName("specification");
			newConstraint.createSpecification(
					"specification", 
					null, 
					UMLFactory.eINSTANCE.getUMLPackage().getOpaqueExpression());					
			newConstraint.setSpecification(constraintBody);
			refresh();
		}
	}

	/** @{inheritDoc} */
	@Override
	public void editItem(TableItem item) {
		if (item.getData(KEY) == null) {
			return;
		}
		
		Constraint constraint = (Constraint) item.getData(KEY);
		InputDialog dialog = 
			new InputDialog(
					new Shell(), 
					"Constraint name : ",
					"Modify constraint name ?",
					constraint.getName(),
					null);
		dialog.open();
		
		if ((dialog.getReturnCode() == InputDialog.OK)
				&& (! dialog.getValue().equals(""))) {
			constraint.setName(dialog.getValue());
			refresh();
		}	
	}
	
	/** @{inheritDoc} */
	@Override
	public void removeButtonPressed() {
		CompoundCommand command = new CompoundCommand(); 
		// get selection and delete it
		TableItem[] tableItems = table.getSelection();
		for(int i = 0; i < tableItems.length ; i++) {
			// get parameter
			Constraint constraint = (Constraint) tableItems[i].getData(KEY);
			command.add(UMLDi2ElementDeleteFactory.eINSTANCE.createCommand(constraint));
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(command);
	}

	/** @{inheritDoc} */
	@Override
	public void downButtonPressed() {
		// Useless : OwnedRules is not an ordered list
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();
		for(int i = (tableItems.length-1); i >= 0; i--) {
			// get parameters list
			List<Constraint> constraints = umlNamespace.getOwnedRules();
			
			// get constraint
			org.eclipse.uml2.uml.Constraint constraint = (Constraint) tableItems[i].getData(KEY);
			int newIndex = table.indexOf(tableItems[i]) + 1;
			if(newIndex < constraints.size()) {
				//new MoveInListCommand(parameters, parameter, newIndex).execute();
				compoundCommand.add(new MoveConstraintInListCommand(constraints, constraint, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
	}

	/** @{inheritDoc} */
	@Override
	public void upButtonPressed() {
		// Useless : OwnedRules is not an ordered list
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();
		List<Constraint> constraints = umlNamespace.getOwnedRules();
		for(int i = 0; i < tableItems.length ; i++) {
			// get constraint
			org.eclipse.uml2.uml.Constraint constraint = (Constraint) tableItems[i].getData(KEY);
			int newIndex = table.indexOf(tableItems[i]) - 1;
			if (newIndex >= 0) {
				compoundCommand.add(new MoveConstraintInListCommand(constraints, constraint, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
	}
	
	/**
	 * Listener for the menu items.
	 */
	public class AddElementSelectionListener implements SelectionListener{

		public AddElementSelectionListener(String type){
			this.constraintType = type;
		}

		public String constraintType;

		/**
		 * Does nothing.
		 * 
		 * @param e SelectionEvent.
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/**
		 * What to do when item selected depending on the type of Behavior we want to associate a BehavioredClassifier to.
		 * 
		 * @param e SelectionEvent.
		 */
		public void widgetSelected(SelectionEvent e){

			Constraint newConstraint = UMLFactory.eINSTANCE.createConstraint();
			newConstraint.setName("Constraint");
			umlNamespace.getOwnedRules().add(newConstraint);
			newConstraint.getConstrainedElements().add(umlNamespace);
			OpaqueExpression constraintBody = UMLFactory.eINSTANCE.createOpaqueExpression(); 
			constraintBody.getLanguages().clear();
			constraintBody.getBodies().clear();
			constraintBody.getLanguages().add("OCL");
			constraintBody.setName("specification");
			newConstraint.createSpecification("specification", null, UMLFactory.eINSTANCE.getUMLPackage().getOpaqueExpression());					
			newConstraint.setSpecification(constraintBody);
			
			newConstraint.getConstrainedElements().add(umlNamespace);
			
			// Set references
			Operation operation = (Operation) umlElement;
			
			if(TYPE_PRE.equals(constraintType)){
				operation.getPreconditions().add(newConstraint);
			}
			else if(TYPE_BODY.equals(constraintType)){
				operation.setBodyCondition(newConstraint);
			}
			else if(TYPE_POST.equals(constraintType)){
				operation.getPostconditions().add(newConstraint);
			}

			refresh();
		}
	}
}
